/**
 * 浅拷贝
 * Object.assign()
 * */
let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target, object2, object3);
console.log(target);  // {a: 1, b: 2, c: 3}
/**
 * 浅拷贝
 * 扩展运算符
 * */
let obj1 = {a: 1, b: {c: 1}}
let obj2 = {...obj1};

/**
 * 数组浅拷贝
 * Array.prototype.slice● 法可以从已有数组中返回选定的元素：用法：array.slice(start, end)，该方法不会改变原始数组 两个参数都可选，如果两个参数都不写，就可以实现一个数组的浅拷贝。
 * Array.prototype.concat ● 此方法不会更改现有数组，而是返回一个新数组。 该方法有两个参数，两个参数都可选，如果两个参数都不写，就可以实现一个数组的浅拷贝。
 * */
//手写浅拷贝
function shallowClone(obj) {
    if (typeof obj !== 'object') {
        throw new Error('obj is not a object');
    }
    let newObj = Array.isArray(obj) ? [] : {};

    for (let key in obj) {
        // for...in 遍历对象时，不仅会遍历对象自身的属性，还会遍历其原型链上的可枚举属性。如果不需要这些继承属性，hasOwnProperty 方法可以确保只复制对象自身的属性。
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}

function deepClone(obj, hash = new WeakMap()) {
    if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
    if (obj instanceof Date) return new Date(obj);
    if (obj instanceof RegExp) return new RegExp(obj);
    // 可能是对象或者普通的值  如果是函数的话是不需要深拷贝
    if (typeof obj !== "object") return obj;
    // 是对象的话就要进行深拷贝
    /**
     * 用于存储已经克隆过的对象及其对应的克隆对象。
     * 它的作用是防止在递归深拷贝过程中遇到循环引用时导致无限递归，从而避免栈溢出错误。
     * 通过检查 hash 是否已经包含当前对象，可以确保每个对象只被克隆一次。
     * */
    if (hash.get(obj)) return hash.get(obj);
    /**
     * 这种设计是为了确保新创建的对象具有与原对象相同的构造函数，从而保留对象的原型链和类型信息。
     * 直接写 let cloneObj = {} 只会创建一个普通的空对象，无法继承原对象的构造函数和原型方法。
     * 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
     * */
    let cloneObj = new obj.constructor();
    //存入哈希表
    hash.set(obj, cloneObj);
    for (let key in obj) {
        /**
         * for...in 遍历对象时，不仅会遍历对象自身的属性，还会遍历其原型链上的可枚举属性。如果不需要这些继承属性，hasOwnProperty 方法可以确保只复制对象自身的属性。
         * */
        if (obj.hasOwnProperty(key)) {
            // 实现一个递归拷贝
            cloneObj[key] = deepClone(obj[key], hash);
        }
    }
    return cloneObj;
}
